package cz.muni.fi.pa165.sme.services.impl;

import java.util.ArrayList;
import java.util.List;

import cz.muni.fi.pa165.sme.api.UserService;
import cz.muni.fi.pa165.sme.api.to.UserTO;
import cz.muni.fi.pa165.sme.common.TOValidator;
import cz.muni.fi.pa165.sme.dao.api.UserDao;
import cz.muni.fi.pa165.sme.dao.entity.User;
import cz.muni.fi.pa165.sme.exceptions.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.convert.ConversionService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author forman
 */
@Service
public class UserServiceImpl implements UserService {

	private static final String NULL_OBJECT_MESSAGE = "Parameter cannot be null";
	private static final String ID_NULL = "Id cannot be null";
	private static final String ID_NOT_NULL = "Id must not be set";

	@Autowired
	private UserDao userDao;

	@Autowired
	@Qualifier("conversionService")
	private ConversionService conversionService;

	@Override
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void createUser(final UserTO userTO) {
		TOValidator.validateObjectNotNull(userTO, NULL_OBJECT_MESSAGE);
		TOValidator.validateIdNull(userTO.getId(), ID_NOT_NULL);

		TOValidator.validateUserCreation(userTO, "User must have login and password specified.");

		final User user = conversionService.convert(userTO, User.class);

		userDao.createUser(user);

		if (user.getId() == null) {
			throw new ServiceException("ID was not assigned");
		}

		userTO.setId(user.getId());
	}

	@Override
	@PreAuthorize("hasRole('ADMIN')")
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void updateUser(final UserTO userTO) {
		TOValidator.validateObjectNotNull(userTO, NULL_OBJECT_MESSAGE);
		TOValidator.validateObjectNotNull(userTO.getId(), ID_NULL);

		final User user = conversionService.convert(userTO, User.class);
		userDao.updateUser(user);
	}

	@Override
	public UserTO getUserTO(final Long id) {
		TOValidator.validateObjectNotNull(id, NULL_OBJECT_MESSAGE);
		final User user = userDao.getUser(id);

		return conversionService.convert(user, UserTO.class);
	}

	@Override
	@PreAuthorize("hasRole('ADMIN')")
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void deleteUserTO(final UserTO userTO) {
		TOValidator.validateObjectNotNull(userTO, NULL_OBJECT_MESSAGE);
		TOValidator.validateObjectNotNull(userTO.getId(), ID_NULL);

		final User user = conversionService.convert(userTO, User.class);
		userDao.deleteUser(user);
	}

	@Override
	@PreAuthorize("hasRole('USER')")
	public List<UserTO> findAllUserTO() {
		final List<User> userResult = userDao.findAllUser();
		final List<UserTO> userTOResult = new ArrayList<UserTO>();

		for (User user : userResult) {
			userTOResult.add(conversionService.convert(user, UserTO.class));
		}

		return userTOResult;
	}

	@Override
	public boolean exists(final UserTO userTO) {
		TOValidator.validateObjectNotNull(userTO, NULL_OBJECT_MESSAGE);

		return userTO.getId() != null && userDao.getUser(userTO.getId()) != null;
	}

	@Override
	public boolean isReferenced(final UserTO userTO) {
		TOValidator.validateObjectNotNull(userTO, NULL_OBJECT_MESSAGE);

		final User userOrig = conversionService.convert(userTO, User.class);

		final List<User> userList = userDao.findAllUser();

		for (User user : userList) {
			if (user.equals(userOrig)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public boolean isLoginAvailable(final String login) {
		TOValidator.validateObjectNotNull(login, NULL_OBJECT_MESSAGE);

		return userDao.getUserByLogin(login) == null;
	}

	@Override
	public UserTO findUserByLogin(final String login) {
		if(login == null) {
			return null;
		}
		final User user = userDao.getUserByLogin(login);
		return conversionService.convert(user, UserTO.class);
	}
}
